/*
 * sample-Setfps.c
 *
 * Copyright (C) 2014 Ingenic Semiconductor Co.,Ltd
 */

#include <imp/imp_log.h>
#include <imp/imp_common.h>
#include <imp/imp_system.h>
#include <imp/imp_framesource.h>
#include <imp/imp_encoder.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#include "sample-common.h"

#define TAG "Sample-ISP"

extern struct chn_conf chn[];
extern int direct_switch;

static int save_stream_by_name(char *stream_prefix, int idx, IMPEncoderStream *stream)
{
	int stream_fd = -1;
	char stream_path[128];
	int ret, i, nr_pack = stream->packCount;

	sprintf(stream_path, "%s_%d", stream_prefix, idx);

	IMP_LOG_DBG(TAG, "Open Stream file %s ", stream_path);
	stream_fd = open(stream_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
	if (stream_fd < 0) {
		IMP_LOG_ERR(TAG, "failed: open %s\n", stream_path);
		return -1;
	}
	IMP_LOG_DBG(TAG, "OK\n");

	for (i = 0; i < nr_pack; i++) {
		ret = write(stream_fd, (void *)stream->pack[i].virAddr, stream->pack[i].length);
		if (ret != stream->pack[i].length) {
			close(stream_fd);
			IMP_LOG_ERR(TAG, "stream write ret(%d) != stream->pack[%d].length(%d)\n", ret, i, stream->pack[i].length);
			return -1;
		}
	}

	close(stream_fd);

	return 0;
}

static int save_stream(int fd, IMPEncoderStream *stream)
{
	int ret, i, nr_pack = stream->packCount;

	for (i = 0; i < nr_pack; i++) {
		ret = write(fd, (void *)stream->pack[i].virAddr,
				stream->pack[i].length);
		if (ret != stream->pack[i].length) {
			IMP_LOG_ERR(TAG, "stream write ret(%d) != stream->pack[%d].length(%d) error\n", ret, i, stream->pack[i].length);
			return -1;
		}
	}
	return 0;
}

static void *h264_stream_thread(void *args)
{
	int val, i, chnNum, ret;
	char stream_path[64];
	IMPPayloadType payloadType;
	int stream_fd = -1, totalSaveCnt = 0;

	val = (int)args;
	chnNum = val & 0xffff;
	payloadType = (val >> 16) & 0xffff;

	ret = IMP_Encoder_StartRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StartRecvPic(%d) failed\n", chnNum);
		return ((void *)-1);
	}

	sprintf(stream_path, "%s/stream-isp-%d.%s", STREAM_FILE_PATH_PREFIX, chnNum,
			(payloadType == PT_H264) ? "h264" : ((payloadType == PT_H265) ? "h265" : "jpeg"));

	if (payloadType == PT_JPEG) {
		totalSaveCnt = ((NR_FRAMES_TO_SAVE / 50) > 0) ? (NR_FRAMES_TO_SAVE / 50) : 1;
	} else {
		IMP_LOG_DBG(TAG, "Video ChnNum=%d Open Stream file %s ", chnNum, stream_path);
		stream_fd = open(stream_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
		if (stream_fd < 0) {
			IMP_LOG_ERR(TAG, "failed: open\n");
			return ((void *)-1);
		}
		IMP_LOG_DBG(TAG, "OK\n");
		totalSaveCnt = NR_FRAMES_TO_SAVE;
	}

	for (i = 0; i < totalSaveCnt; i++) {
		ret = IMP_Encoder_PollingStream(chnNum, 1000);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_PollingStream(%d) timeout\n", chnNum);
			continue;
		}

		IMPEncoderStream stream;
		/* Get H264 or H265 Stream */
		ret = IMP_Encoder_GetStream(chnNum, &stream, 1);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_GetStream(%d) failed\n", chnNum);
			return ((void *)-1);
		}
		//IMP_LOG_DBG(TAG, "i=%d, stream.packCount=%d, stream.%sRefType=%d\n", i, stream.packCount,
		//payloadtype == PT_H264 ? "h264" : "h265", payloadType == PT_H264 ? stream.h264RefType : stream.h265RefType);

		if (payloadType == PT_JPEG) {
			ret = save_stream_by_name(stream_path, i, &stream);
			if (ret < 0) {
				return ((void *)ret);
			}
		}
#if 1
		else {
			ret = save_stream(stream_fd, &stream);
			if (ret < 0) {
				close(stream_fd);
				return ((void *)ret);
			}
		}
#endif
		IMP_Encoder_ReleaseStream(chnNum, &stream);
	}

	close(stream_fd);

	ret = IMP_Encoder_StopRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StopRecvPic(%d) failed\n", chnNum);
		return ((void *)-1);
	}

	return ((void *)0);
}

int read_isp_w02(void) {
	int result = 0;
	char buf[256] = { '\0' };
	char delims[] = ",";
	char m0_path[128] = "/proc/jz/isp/isp-w02";
	int fd = 0;
	fd = open(m0_path, O_RDONLY);
	if(fd < 0) {
		printf("open %s error\n", m0_path);
		return -1;
	}

	memset(buf, 0, sizeof(buf));
	read(fd, buf, sizeof(buf));

	result = atoi(strtok(buf, delims));

	close(fd);

	return result;
}

int main(int argc, char *argv[])
{
	int i, ret, fps_num, fps_den;
	int change_cnt = 20;
	if(argc > 1) {
		change_cnt = atoi(argv[1]);
	}

	IMPISPTuningOpsMode mode;
	IMPISPRunningMode runing_mode;

	/* Step.1 System init */
	ret = sample_system_init();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_System_Init() failed\n");
		return -1;
	}

	/* Step.2 FrameSource init */
	/* frame per second */
	// init isp
	fps_num = 10;
	fps_den = 1;
	ret = IMP_ISP_Tuning_SetSensorFPS(fps_num, fps_den);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "ISP Set fps failed\n");
		return -1;
	}
	mode = IMPISP_TUNING_OPS_MODE_DISABLE;
	ret = IMP_ISP_Tuning_SetISPHflip(mode);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "ISP Set Hflip failed\n");
		return -1;
	}
	if(!direct_switch) {
		mode = IMPISP_TUNING_OPS_MODE_DISABLE;
		ret = IMP_ISP_Tuning_SetISPVflip(mode);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "ISP Set Vflip failed\n");
			return -1;
		}
	}
	runing_mode = IMPISP_RUNNING_MODE_DAY;
	IMP_ISP_Tuning_SetISPRunningMode(runing_mode);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "ISP Set Runing mode failed\n");
		return -1;
	}

	ret = sample_framesource_init();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "FrameSource init failed\n");
		return -1;
	}

	for (i = 0; i < FS_CHN_NUM; i++) {
		if (chn[i].enable) {
			ret = IMP_Encoder_CreateGroup(chn[i].index);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "IMP_Encoder_CreateGroup(%d) error !\n", i);
				return -1;
			}
		}
	}

	/* Step.3 Encoder init */
	ret = sample_encoder_init();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "Encoder init failed\n");
		return -1;
	}

	/* Step.4 Bind */
	for (i = 0; i < FS_CHN_NUM; i++) {
		if (chn[i].enable) {
			ret = IMP_System_Bind(&chn[i].framesource_chn, &chn[i].imp_encoder);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "Bind FrameSource channel%d and Encoder failed\n",i);
				return -1;
			}
		}
	}

	/* Step.5 Stream On */
	ret = sample_framesource_streamon();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "ImpStreamOn failed\n");
		return -1;
	}
#if 1
	/* Step.6 Get stream */
	pthread_t tid; /* Stream capture in another thread */
	ret = pthread_create(&tid, NULL, h264_stream_thread, NULL);
	if (ret) {
		IMP_LOG_ERR(TAG, "h264 stream create error\n");
		return -1;
	}
#endif
	static int count = 0;
	int sleep_time = 1;
	while(count < change_cnt){
		sleep(sleep_time);
		uint32_t fps[4] = {10, 15, 20, 25};
		static int fps_flag = 0;
		fps_num = fps[fps_flag % 4];
		fps_den = 1;
		ret = IMP_ISP_Tuning_SetSensorFPS(fps_num, fps_den);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "ISP Set fps failed\n");
			return -1;
		}

		sleep(sleep_time);
		static int status = 0;
		IMPISPTuningOpsMode hFlip_mode, vFlip_mode;	
		hFlip_mode = vFlip_mode =  (!status) ? IMPISP_TUNING_OPS_MODE_ENABLE : IMPISP_TUNING_OPS_MODE_DISABLE;
		ret = IMP_ISP_Tuning_SetISPHflip(hFlip_mode);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "ISP Set Hflip failed\n");
			return -1;
		}
		if(!direct_switch) {
			sleep(sleep_time);
			ret = IMP_ISP_Tuning_SetISPVflip(vFlip_mode);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "ISP Set Vflip failed\n");
				return -1;
			}
		}

		runing_mode = (!status) ? IMPISP_RUNNING_MODE_NIGHT : IMPISP_RUNNING_MODE_DAY;
		sleep(sleep_time);
		IMP_ISP_Tuning_SetISPRunningMode(runing_mode);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "ISP Set Runing mode failed\n");
			return -1;
		}

		sleep(3);
		// check code
#if 0
		uint32_t cur_fps_num, cur_fps_den;
		ret = IMP_ISP_Tuning_GetSensorFPS(&cur_fps_num, &cur_fps_den);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "ISP Get fps failed\n");
			return -1;
		}
#endif
		int first = 0, second = 0;	
		first = read_isp_w02();
		if(first == -1){
			IMP_LOG_ERR(TAG, "read isp_w02 failed\n");
			return -1;
		}
		sleep(1);
		second = read_isp_w02();
		if(second == -1){
			IMP_LOG_ERR(TAG, "read isp_w02 failed\n");
			return -1;
		}
		printf("get fps = %d\n", second - first);

		IMPISPTuningOpsMode cur_hFlip_mode;
		IMPISPTuningOpsMode cur_vFlip_mode;
		IMPISPRunningMode cur_runing_mode;
		ret = IMP_ISP_Tuning_GetISPHflip(&cur_hFlip_mode);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "ISP Get Hflip failed\n");
			return -1;
		}
		if(!direct_switch) {
			sleep(sleep_time);
			ret = IMP_ISP_Tuning_GetISPVflip(&cur_vFlip_mode);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "ISP Get Vflip failed\n");
				return -1;
			}
		}

		ret = IMP_ISP_Tuning_GetISPRunningMode(&cur_runing_mode);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "ISP Get Runing mode failed\n");
			return -1;
		}

		int vFlip_mode_flag = 0;
		if(!direct_switch){
			if(vFlip_mode == cur_hFlip_mode) {
				vFlip_mode_flag = 1;
			} else {
				vFlip_mode_flag = 0;
			}
		} else {
			vFlip_mode_flag = 1;
		}

		if(hFlip_mode == cur_hFlip_mode && (fps_num/fps_den > (second-first)*9/10 && fps_num/fps_den < (second-first)*11/10 ) && cur_runing_mode == runing_mode && vFlip_mode_flag) {
			printf("\033[1;31mchange cur cnt = %d, total cnt = %d \033[0m\n", count, change_cnt);
		} else {
			printf("\033[1;31mchange error, cur cnt = %d, fps[%d:%d], hflipmode[%d:%d], runing_mode[%d:%d] \033[0m\n",
					count,
					fps_num/fps_den, second-first,
					hFlip_mode, cur_hFlip_mode,
					runing_mode, cur_runing_mode);

			int stream_fd = -1; 
			stream_fd = open("/tmp/log.txt", O_RDWR | O_CREAT | O_APPEND, 0644);
			if (stream_fd < 0) {
				IMP_LOG_ERR(TAG, "failed open fd!\n");
				return -1; 
			}   
			char error[128];
			sprintf(error, "change ISP Effect Error!!!!\n");
			ret = write(stream_fd, error, 28);
			if (ret == -1) {
				IMP_LOG_ERR(TAG, "failed write! \n");
				return -1; 
			}   
			close(stream_fd);
		}
		fps_flag++;
		count++;
		status = !status;
	}

/* Exit sequence as follow */
//pthread_join(tid, NULL);

	/* Step.a Stream Off */
	ret = sample_framesource_streamoff();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "FrameSource StreamOff failed\n");
		return -1;
	}

	/* Step.b UnBind */
	for (i = 0; i < FS_CHN_NUM; i++) {
		if (chn[i].enable) {
			ret = IMP_System_UnBind(&chn[i].framesource_chn, &chn[i].imp_encoder);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "UnBind FrameSource channel%d and Encoder failed\n",i);
				return -1;
			}
		}
	}

	/* Step.c Encoder exit */
	ret = sample_encoder_exit();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "Encoder exit failed\n");
		return -1;
	}

	/* Step.d FrameSource exit */
	ret = sample_framesource_exit();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "FrameSource exit failed\n");
		return -1;
	}

	/* Step.e System exit */
	ret = sample_system_exit();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "sample_system_exit() failed\n");
		return -1;
	}

	int stream_fd = -1;
	stream_fd = open("/tmp/log.txt", O_RDWR | O_CREAT | O_APPEND, 0644);
	if (stream_fd < 0) {
		IMP_LOG_ERR(TAG, "failed open fd!\n");
		return -1;
	}
	char error[128];
	sprintf(error, "ISP Moudles Run Success!!!!\n");
	ret = write(stream_fd, error, 28);
	if (ret == -1) {
		IMP_LOG_ERR(TAG, "failed write! \n");
		return -1;
	}
	close(stream_fd);

	return 0;
}
